package com.github.m6d21.sso.support.aspect;

import com.github.m6d21.sso.common.util.AesUtil;
import com.github.m6d21.sso.common.util.AnnotationHelper;
import com.github.m6d21.sso.core.annotation.Encrypt;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import org.apache.commons.collections.CollectionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.stereotype.Component;

/**
 *<p>
 *
 *</p>
 *
 * @author liuyang
 * @date 2019/1/2 Wed 19:41:00
 * @since 1.0.0
 */
@Aspect
@Component
public class CryptAspect {

    private static final Logger LOG = LoggerFactory.getLogger(CryptAspect.class);

    @Pointcut("@annotation(com.github.m6d21.sso.core.annotation.Crypt)")
    public void pcCrypt() {
    }

    @Around("pcCrypt()")
    public Object processCrypt(ProceedingJoinPoint joinPoint) throws Throwable {
        //执行方法前对参数进行加密
        MethodInvocationProceedingJoinPoint methodJoinPoint = (MethodInvocationProceedingJoinPoint) joinPoint;
        Field proxy = methodJoinPoint.getClass().getDeclaredField("methodInvocation");
        proxy.setAccessible(true);
        ReflectiveMethodInvocation j = (ReflectiveMethodInvocation) proxy.get(methodJoinPoint);
        Method method = j.getMethod();
        //获取参数类型
        Parameter[] parameters = Optional.ofNullable(method.getParameters()).orElse(new Parameter[0]);
        //获取参数值
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < parameters.length; i++) {
            Encrypt encrypt = parameters[i].getAnnotation(Encrypt.class);
            if (encrypt == null) {
                continue;
            }
            Object arg = args[i];
            if (arg instanceof String) {
                //对参数进行加密
                args[i] = AesUtil.encrypt((String) arg);
            } else {
                AnnotationHelper annotationHelper = AnnotationHelper.newInstance();
                List<Entry<Annotation, Field>> annotationEntryList = annotationHelper
                    .findAnnotationsOnField(arg.getClass(), Encrypt.class);
                if (CollectionUtils.isNotEmpty(annotationEntryList)) {
                    annotationEntryList.forEach(entry -> {
                        injectField(arg, entry);
                    });
                }
            }
        }

        //执行方法
        return joinPoint.proceed(args);
    }

    private void injectField(Object arg, Entry<Annotation, Field> entry) {
        Field field = entry.getValue();
        if (field != null) {
            field.setAccessible(true);
            if (String.class.equals(field.getType())) {
                try {
                    field.set(arg, AesUtil.encrypt((String) field.get(arg)));
                } catch (IllegalAccessException e) {
                    LOG.error("加密对象中的参数失败, error: {}", e);
                }
            }
        }
    }

}

